`timescale 1ns/1ns

// 仅为演示采样时机, 仿真器行为天差地别, 切勿用到项目中 !!!

module coverage_sampling_timing_2;

    bit clk;

    int data_1step;
    int data_0skew;

    // 辅助函数, 用于在采样时打印哪个 covergroup 在何时采样了哪个值
    function automatic bit print_sampled_value(string cg_name, string cp_name, int val);
        $display("@%0t\t[COVERAGE LOG] -> %s is sampling '%s', value = %0d", $time, cg_name, cp_name, val);
        return 1; // 必须返回 1, 否则 iff 条件为假, 采样会被阻止
    endfunction

    // clocking block: 使用 #1step 采样
    // 在 Preponed 区域进行采样
    clocking cb_1step @(posedge clk);
        input #1step data_1step;
    endclocking

    // clocking block: 使用 #0 采样
    // 在 Observed 区域进行采样
    clocking cb_0skew @(posedge clk);
        input #0     data_0skew;
    endclocking

    // @(cb_1step) 表示 cg_1step 在 cb_1step 的时钟事件发生时进行采样
    covergroup cg_1step @(cb_1step);
        option.per_instance = 1;
        cp_data_1step: coverpoint cb_1step.data_1step iff (print_sampled_value("cg", "data_1step", cb_1step.data_1step)) {
            bins b_100 = {100}; // 旧值
            bins b_200 = {200}; // 新值
        }
    endgroup

    // @(cb_0skew) 表示 cg_0skew 在 cb_0skew 的时钟事件发生时进行采样
    covergroup cg_0skew @(cb_0skew);
        option.per_instance = 1;
        cp_data_0skew: coverpoint cb_0skew.data_0skew iff (print_sampled_value("cg", "data_0skew", cb_0skew.data_0skew)) {
            bins b_100 = {100}; // 旧值
            bins b_200 = {200}; // 新值
        }
    endgroup

    // 实例化 covergroup
    cg_1step cg_inst_1step = new;
    cg_0skew cg_inst_0skew = new;

    // 时钟生成
    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end

    always @(posedge clk) begin
        // 在 Active Region 更新
        data_1step = 200;
        data_0skew = 200;
        $display("@%0t\talways @(posedge clk) data_1step = %d, data_0skew = %d", $time, data_1step, data_0skew);
    end

    initial begin
        data_1step = 100;
        $display("@%0t\tdata_1step is %d", $time, data_1step);

        @(cb_1step);
        $display("@%0t\tdata_1step is %d", $time, data_1step);
    end

    initial begin
        data_0skew = 100;
        $display("@%0t\tdata_0skew is %d", $time, data_0skew);

        @(cb_0skew);
        $display("@%0t\tdata_0skew is %d", $time, data_0skew);
    end

    initial begin
        #12 $finish;
    end
endmodule

/* Output: VCS
@0      data_1step is         100
@0      data_0skew is         100
@5      always @(posedge clk) data_1step =         200, data_0skew =         200
@5      [COVERAGE LOG] -> cg is sampling 'data_0skew', value = 200
@5      data_0skew is         200
@5      [COVERAGE LOG] -> cg is sampling 'data_1step', value = 100
@5      data_1step is         200
*/

/* Output: QuestaSim [重复输出]
# @0    data_1step is         100
# @0    data_0skew is         100
# @5    always @(posedge clk) data_1step =         200, data_0skew =         200
# @5    [COVERAGE LOG] -> cg is sampling 'data_1step', value = 100
# @5    [COVERAGE LOG] -> cg is sampling 'data_1step', value = 100
# @5    [COVERAGE LOG] -> cg is sampling 'data_0skew', value = 200
# @5    [COVERAGE LOG] -> cg is sampling 'data_0skew', value = 200
# @5    data_1step is         200
# @5    data_0skew is         200
*/
